#include <stdlib.h> // for malloc,free
#include <memory.h> // for memcpy
#include <new> // for new()
#include "opus/opus.h"
#define MINIAUDIO_IMPLEMENTATION
#ifdef _WIN32
#define MA_ENABLE_ONLY_SPECIFIC_BACKENDS
#define MA_ENABLE_WASAPI
#endif
#define MA_NO_ENCODING
#define MA_NO_FLAC
#define MA_NO_MP3
#define MA_NO_RESOURCE_MANAGER
#define MA_NO_GENERATION
#include "miniaudio.h"

extern "C" __declspec(dllexport) int __cdecl maEngineInit(ma_engine** const ppEngine) {
	if (!ppEngine)
		return 1;
	*ppEngine = 0;
	ma_engine* const pEngine = (ma_engine*)malloc(sizeof(ma_engine));
	if (!pEngine)
		return 2;
	const ma_engine_config ec = ma_engine_config_init();
	const ma_result result = ma_engine_init(&ec, pEngine);
	if (result != MA_SUCCESS) {
		free(pEngine);
		return result;
	}
	*ppEngine = pEngine;
	return MA_SUCCESS;
}

extern "C" __declspec(dllexport) void __cdecl maEngineDestroy(ma_engine* const pEngine) {
	ma_engine_uninit(pEngine);
	free(pEngine);
}

enum {
	TYPE_WAV = 0,
	TYPE_OPUS = 1,
};

struct Sound {
	const int type;
	ma_sound maSound;
	Sound(int type_) : type(type_) {}
	Sound(const Sound&) = delete;
	Sound(const Sound&&) = delete;
	Sound& operator=(const Sound&) = delete;
	Sound& operator=(const Sound&&) = delete;
};

struct SoundWav : Sound {
	ma_wav maWav;
	SoundWav() : Sound(TYPE_WAV) {}
};

constexpr ma_uint32 OPUS_SAMPLE_RATE     = 48000;
constexpr ma_uint32 OPUS_FRAME_TIME_MS   = 120;
constexpr ma_uint32 OPUS_CHANNELS        = 2;
constexpr ma_format OPUS_SAMPLE_TYPE     = ma_format_f32;
constexpr ma_uint32 OPUS_ONE_FRAME_SIZE  = sizeof(float) * OPUS_CHANNELS; // 8b = 4b/c * 2c
constexpr ma_uint32 OPUS_PACKET_FRAMES   = OPUS_SAMPLE_RATE * OPUS_FRAME_TIME_MS / 1000; // 5760f/p = 48000f/s * 0.12s/p
constexpr ma_uint32 OPUS_PRE_SKIP_FRAMES = OPUS_SAMPLE_RATE * 65 / 10000; // 312f = 48000f/s * 0.0065s

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4200) // nonstandard extension used: zero-sized array in struct/union
#endif
struct SoundOpus : Sound {
	ma_data_source_base ds;
	OpusDecoder* const pDecoder;
	const ma_uint8* const pDataBase;
	const ma_uint64 totalFrames; // not include PreSkip frames
	const ma_uint32 packetCount;
	ma_uint32 curPacketIdx; // [0,packetCount)
	ma_uint64 curSeekFrame; // [0,totalFrames]
	float decBuf[OPUS_PACKET_FRAMES * OPUS_CHANNELS]; // [5760 * 2] = 48000f/s * 0.12s * 2c
	ma_uint32 nextPacketOffsets[0]; // [packetCount] (bytes*)
	SoundOpus(OpusDecoder* pDecoder_, const ma_uint8* pDataBase_, ma_uint64 totalFrames_, ma_uint32 packetCount_)
		: Sound(TYPE_OPUS), pDecoder(pDecoder_), pDataBase(pDataBase_), totalFrames(totalFrames_), packetCount(packetCount_),
		curPacketIdx(-2), curSeekFrame(0) {}
};
#ifdef _MSC_VER
#pragma warning(pop)
#endif

static inline SoundOpus* fromDataSource(ma_data_source* pDs) {
	return (SoundOpus*)((ma_uint8*)pDs - (size_t)&((SoundOpus*)0)->ds);
}

// #include <stdio.h>
static ma_result maDsRead(ma_data_source* const pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* const pFramesRead) {
	// printf("maDsRead: begin: %p [%llu]\n", pFramesOut, frameCount);
	ma_uint64 outFrames = 0;
	if (frameCount > 0) {
		if (!pDataSource || !pFramesOut)
			return MA_INVALID_ARGS;
		SoundOpus* const pSound = fromDataSource(pDataSource);
		// printf("maDsRead: begin: %u, %llu / %llu\n", pSound->curPacketIdx, pSound->curSeekFrame, pSound->totalFrames);
		const ma_uint64 absSeekFrame = OPUS_PRE_SKIP_FRAMES + pSound->curSeekFrame;
		ma_uint32 seekPacketIdx = (ma_uint32)(absSeekFrame / OPUS_PACKET_FRAMES);
		ma_uint32 seekInPacketFrame = absSeekFrame % OPUS_PACKET_FRAMES;
		if (frameCount > pSound->totalFrames - pSound->curSeekFrame)
			frameCount = pSound->totalFrames - pSound->curSeekFrame;
		for (; frameCount > 0; seekPacketIdx++, seekInPacketFrame = 0) {
			if (seekPacketIdx != pSound->curPacketIdx) {
				ma_uint32 packetOffset;
				if (seekPacketIdx == pSound->curPacketIdx + 1)
					packetOffset = pSound->nextPacketOffsets[pSound->curPacketIdx];
				else if (seekPacketIdx == 0) {
					packetOffset = 0;
					// printf("maDsRead: opus_decoder_ctl\n");
					opus_decoder_ctl(pSound->pDecoder, OPUS_RESET_STATE);
				} else { // decode one previous packet for pre-roll should be enough
					const ma_uint32 prePacketOffset = seekPacketIdx == 1 ? 0 : pSound->nextPacketOffsets[seekPacketIdx - 2];
					packetOffset = pSound->nextPacketOffsets[seekPacketIdx - 1];
					// printf("maDsRead: opus_decode_float(pre): idx=%u, off=%u, len=%u\n", seekPacketIdx-1, prePacketOffset, packetOffset - prePacketOffset);
					opus_decode_float(pSound->pDecoder, pSound->pDataBase + prePacketOffset, packetOffset - prePacketOffset,
						pSound->decBuf, OPUS_PACKET_FRAMES, 0);
				}
				// printf("maDsRead: opus_decode_float: idx=%u, off=%u, len=%u\n", seekPacketIdx, packetOffset, pSound->nextPacketOffsets[seekPacketIdx] - packetOffset);
				opus_decode_float(pSound->pDecoder, pSound->pDataBase + packetOffset,
					pSound->nextPacketOffsets[seekPacketIdx] - packetOffset, pSound->decBuf, OPUS_PACKET_FRAMES, 0);
				pSound->curPacketIdx = seekPacketIdx;
			}
			const ma_uint64 copyFrames = ma_min(frameCount, OPUS_PACKET_FRAMES - seekInPacketFrame);
			memcpy(pFramesOut, pSound->decBuf + seekInPacketFrame * OPUS_CHANNELS, copyFrames * OPUS_ONE_FRAME_SIZE);
			pFramesOut = (ma_uint8*)pFramesOut + copyFrames * OPUS_ONE_FRAME_SIZE;
			outFrames += copyFrames;
			frameCount -= copyFrames;
		}
		pSound->curSeekFrame += outFrames;
		// printf("maDsRead: end: %llu => %llu\n", outFrames, pSound->curSeekFrame);
	}
	if (pFramesRead)
		*pFramesRead = outFrames;
	return MA_SUCCESS;
}

static ma_result maDsSeek(ma_data_source* const pDataSource, const ma_uint64 frameIndex) {
	if (!pDataSource)
		return MA_INVALID_ARGS;
	SoundOpus* const pSound = fromDataSource(pDataSource);
	pSound->curSeekFrame = frameIndex <= pSound->totalFrames ? frameIndex : pSound->totalFrames;
	// printf("maDsSeek: %llu\n", frameIndex);
	return MA_SUCCESS;
}

static ma_result maDsGetDataFormat(ma_data_source* const pDataSource, ma_format* const pFormat,
	ma_uint32* const pChannels, ma_uint32* const pSampleRate, ma_channel* const pChannelMap, const size_t channelMapCap) {
	if (pFormat)
		*pFormat = OPUS_SAMPLE_TYPE;
	if (pChannels)
		*pChannels = OPUS_CHANNELS;
	if (pSampleRate)
		*pSampleRate = OPUS_SAMPLE_RATE;
	ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, OPUS_CHANNELS);
	return MA_SUCCESS;
}

static ma_result maDsGetCursor(ma_data_source* const pDataSource, ma_uint64* const pCursor) {
	if (!pDataSource || !pCursor)
		return MA_INVALID_ARGS;
	*pCursor = fromDataSource(pDataSource)->curSeekFrame;
	return MA_SUCCESS;
}

static ma_result maDsGetLength(ma_data_source* const pDataSource, ma_uint64* const pLength) {
	if (!pDataSource || !pLength)
		return MA_INVALID_ARGS;
	*pLength = fromDataSource(pDataSource)->totalFrames;
	return MA_SUCCESS;
}

static const ma_data_source_vtable maDsVtable = {
	maDsRead,
	maDsSeek,
	maDsGetDataFormat,
	maDsGetCursor,
	maDsGetLength,
	0, 0
};

// 0000_0xxx xxxx_xxxx: 13-bit(BE) absolute size; other first byte: signed(b-0x84) [-124,123] is relative size to the last
static inline int decodePayloadSize(const ma_uint8** pp, const int last) {
	const ma_uint8* const p = *pp;
	const int b = *p;
	if (b > 7) {
		*pp = p + 1;
		return last + b - 0x84;
	}
	*pp = p + 2;
	return (b << 8) + p[1];
}

extern "C" __declspec(dllexport) int __cdecl
maEngineLoadSound(ma_engine* const pEngine, const ma_uint8* const pData, const ma_uint64 dataSize, Sound** const ppSound) {
	if (!ppSound)
		return 1;
	*ppSound = 0;
	if (!pEngine)
		return 2;
	if (!pData)
		return 3;
	if (dataSize < 3)
		return 4;

	if (dataSize >= 4 && *(const ma_uint32*)pData == *(const ma_uint32*)"RIFF") {
		void* const p = malloc(sizeof(SoundWav));
		if (!p)
			return 5;
		SoundWav* const pSoundWav = new(p) SoundWav;
		const ma_decoding_backend_config dbc = ma_decoding_backend_config_init(ma_format_unknown, 0);
		ma_result result = ma_wav_init_memory(pData, dataSize, &dbc, 0, &pSoundWav->maWav);
		if (result != MA_SUCCESS) {
			free(p);
			return result & 0xffff;
		}
		result = ma_sound_init_from_data_source(pEngine, &pSoundWav->maWav.ds, 0, 0, &pSoundWav->maSound);
		if (result != MA_SUCCESS) {
			ma_wav_uninit(&pSoundWav->maWav, 0);
			free(p);
			return (result & 0xffff) + (1 << 16);
		}
		*ppSound = pSoundWav;
	} else { // .opa
		const ma_uint64 totalFrames = *(const ma_uint32*)(pData + 4); // pData: [4]magic+profile; [4]totalFrames; {PayLoadSize}*; {PayLoadData}*
		const ma_uint64 packets = totalFrames > 0 ? (OPUS_PRE_SKIP_FRAMES + totalFrames + OPUS_PACKET_FRAMES - 1) / OPUS_PACKET_FRAMES : 0;
		const ma_uint8* pSrc = pData + 8;
		const ma_uint8* const qSrc = pData + dataSize;
		int last = 0;
		for (ma_uint64 s = 0, i = 0; i < packets; s += last, i++) {
			if (pSrc + 2 + s > qSrc || (last = decodePayloadSize(&pSrc, last)) <= 0)
				return 7; // malformed data
		}
		int err;
		OpusDecoder* const pDecoder = opus_decoder_create(OPUS_SAMPLE_RATE, OPUS_CHANNELS, &err);
		if (err != OPUS_OK || !pDecoder)
			return (err & 0xffff) + (2 << 16);
		void* const p = malloc(sizeof(SoundOpus) + sizeof(ma_uint32) * packets);
		if (!p) {
			opus_decoder_destroy(pDecoder);
			return 9;
		}
		SoundOpus* const pSoundOpus = new(p) SoundOpus(pDecoder, pSrc, totalFrames, (ma_uint32)packets);
		last = 0;
		pSrc = pData + 8;
		for (ma_uint64 s = 0, i = 0; i < packets; i++)
			pSoundOpus->nextPacketOffsets[i] = (ma_uint32)(s += (last = decodePayloadSize(&pSrc, last)));
		ma_data_source_config dsc = ma_data_source_config_init();
		dsc.vtable = &maDsVtable;
		ma_result result = ma_data_source_init(&dsc, &pSoundOpus->ds);
		if (result != MA_SUCCESS) {
			opus_decoder_destroy(pDecoder);
			free(p);
			return (result & 0xffff) + (3 << 16);
		}
		result = ma_sound_init_from_data_source(pEngine, &pSoundOpus->ds, 0, 0, &pSoundOpus->maSound);
		if (result != MA_SUCCESS) {
			ma_data_source_uninit(&pSoundOpus->ds);
			opus_decoder_destroy(pDecoder);
			free(p);
			return (result & 0xffff) + (4 << 16);
		}
		// printf("maEngineLoadSound: %llu, %llu\n", totalFrames, pSoundOpus->totalFrames);
		*ppSound = pSoundOpus;
	}
	return MA_SUCCESS;
}

extern "C" __declspec(dllexport) int __cdecl maSoundDestroy(Sound* const pSound) {
	if (!pSound)
		return 1;
	ma_sound_uninit(&pSound->maSound);
	switch (pSound->type) {
	case TYPE_WAV:
		ma_wav_uninit(&((SoundWav*)pSound)->maWav, 0);
		break;
	case TYPE_OPUS: {
		SoundOpus* const pSoundOpus = (SoundOpus*)pSound;
		ma_data_source_uninit(&pSoundOpus->ds);
		opus_decoder_destroy(pSoundOpus->pDecoder);
		break;
	}
	default:
		return 2;
	}
	free(pSound);
	return MA_SUCCESS;
}

extern "C" __declspec(dllexport) int __cdecl maSoundSetPos(Sound* const pSound, const ma_uint64 pos);

extern "C" __declspec(dllexport) int __cdecl maSoundPlay(Sound* const pSound) {
	int result = maSoundSetPos(pSound, 0);
	if (result != MA_SUCCESS)
		return result & 0xffff;
	result = ma_sound_start(&pSound->maSound);
	if (result != MA_SUCCESS)
		return (result & 0xffff) + (1 << 16);
	return MA_SUCCESS;
}

extern "C" __declspec(dllexport) int __cdecl maSoundStop(Sound* const pSound) {
	if (!pSound)
		return 1;
	return ma_sound_stop(&pSound->maSound);
}

extern "C" __declspec(dllexport) int __cdecl maSoundPause(Sound* const pSound) {
	if (!pSound)
		return 1;
	return ma_sound_stop(&pSound->maSound);
}

extern "C" __declspec(dllexport) int __cdecl maSoundResume(Sound* const pSound) {
	if (!pSound)
		return 1;
	return ma_sound_start(&pSound->maSound);
}

extern "C" __declspec(dllexport) int __cdecl maSoundSetVolume(Sound* const pSound, const float volume) {
	if (!pSound)
		return 1;
	ma_sound_set_volume(&pSound->maSound, volume);
	return MA_SUCCESS;
}

extern "C" __declspec(dllexport) int __cdecl maSoundSetPan(Sound* const pSound, const float pan) {
	if (!pSound)
		return 1;
	ma_sound_set_pan(&pSound->maSound, pan);
	return MA_SUCCESS;
}

extern "C" __declspec(dllexport) int __cdecl maSoundSetPitch(Sound* const pSound, const float pitch) {
	if (!pSound)
		return 1;
	ma_sound_set_pitch(&pSound->maSound, pitch);
	return MA_SUCCESS;
}

extern "C" __declspec(dllexport) int __cdecl maSoundSetPos(Sound* const pSound, const ma_uint64 pos) {
	if (!pSound)
		return 1;
	ma_data_source* pDs;
	switch (pSound->type) {
	case TYPE_WAV:
		pDs = &((SoundWav*)pSound)->maWav.ds;
		break;
	case TYPE_OPUS:
		pDs = &((SoundOpus*)pSound)->ds;
		break;
	default:
		return 2;
	}
	return ma_data_source_seek_to_pcm_frame(pDs, pos);
}

extern "C" __declspec(dllexport) int __cdecl maSoundSetRange(Sound* const pSound, const ma_uint64 beginPos, const ma_uint64 endPos) {
	if (!pSound)
		return 1;
	ma_data_source* pDs;
	switch (pSound->type) {
	case TYPE_WAV:
		pDs = &((SoundWav*)pSound)->maWav.ds;
		break;
	case TYPE_OPUS:
		pDs = &((SoundOpus*)pSound)->ds;
		break;
	default:
		return 2;
	}
	if (ma_data_source_is_looping(pDs)) {
		ma_result result = ma_data_source_set_loop_point_in_pcm_frames(pDs, beginPos, endPos);
		if (result != MA_SUCCESS)
			return result & 0xffff;
	} else {
		ma_result result = ma_data_source_set_range_in_pcm_frames(pDs, beginPos, endPos);
		if (result != MA_SUCCESS)
			return (result & 0xffff) + (1 << 16);
	}
	return MA_SUCCESS;
}

extern "C" __declspec(dllexport) int __cdecl maSoundSetLoop(Sound* const pSound, const int loop) {
	if (!pSound)
		return 1;
	ma_data_source* pDs;
	switch (pSound->type) {
	case TYPE_WAV:
		pDs = &((SoundWav*)pSound)->maWav.ds;
		break;
	case TYPE_OPUS:
		pDs = &((SoundOpus*)pSound)->ds;
		break;
	default:
		return 2;
	}
	return ma_data_source_set_looping(pDs, loop);
}

extern "C" __declspec(dllexport) int __cdecl maSoundGetTotalFrames(Sound* const pSound, ma_uint64* const pFrames) {
	if (!pFrames)
		return 1;
	*pFrames = 0;
	if (!pSound)
		return 2;
	ma_data_source* pDs;
	switch (pSound->type) {
	case TYPE_WAV:
		pDs = &((SoundWav*)pSound)->maWav.ds;
		break;
	case TYPE_OPUS:
		pDs = &((SoundOpus*)pSound)->ds;
		break;
	default:
		return 3;
	}
	return ma_data_source_get_length_in_pcm_frames(pDs, pFrames);
}

extern "C" __declspec(dllexport) int __cdecl maSoundGetSampleRate(Sound* const pSound, int* const pSampleRate) {
	if (!pSampleRate)
		return 1;
	*pSampleRate = 0;
	if (!pSound)
		return 2;
	ma_data_source* pDs;
	switch (pSound->type) {
	case TYPE_WAV:
		pDs = &((SoundWav*)pSound)->maWav.ds;
		break;
	case TYPE_OPUS:
		pDs = &((SoundOpus*)pSound)->ds;
		break;
	default:
		return 3;
	}
	return ma_data_source_get_data_format(pDs, 0, 0, (ma_uint32*)pSampleRate, 0, 0);
}

extern "C" __declspec(dllexport) int __cdecl maSoundGetPos(Sound* const pSound, ma_uint64* const pPos) {
	if (!pPos)
		return 1;
	*pPos = 0;
	if (!pSound)
		return 2;
	ma_data_source* pDs;
	switch (pSound->type) {
	case TYPE_WAV:
		pDs = &((SoundWav*)pSound)->maWav.ds;
		break;
	case TYPE_OPUS:
		pDs = &((SoundOpus*)pSound)->ds;
		break;
	default:
		return 3;
	}
	return ma_data_source_get_cursor_in_pcm_frames(pDs, pPos);
}

static inline int decodeOpusPayloadMs2(const ma_uint8* const payloadData) {
	int n, b0 = payloadData[0];
	switch (b0 & 3) {
	case 0: n = 1; break; // one frame
	case 1: case 2: n = 2; break; // two cbr/vbr frames
	default: n = payloadData[1] & 0x3f; break; // 0~63, multiple cbr/vbr(payloadData[1]&0x80) frames
	}
	int m = (b0 >> 3) & 3;
	if (b0 & 0x80) // CELT_ONLY
		m = (2000 << m) / 400; // 2.5,5,10,20
	else if ((b0 & 0x60) == 0x60) // HYBRID
		m = 20 << (m & 1);
	else // SILK_ONLY
		m = m == 3 ? 120 : 20 << m;
	return n * m;
}

static inline void writeWavHead(ma_uint8* const pHead, const ma_uint32 sampleRate, const ma_uint32 channels, const ma_uint32 waveSize) {
	*(ma_uint32*)(pHead + 0x00) = *(const ma_uint32*)"RIFF";
	*(ma_uint32*)(pHead + 0x04) = waveSize + 0x2c - 8;
	*(ma_uint64*)(pHead + 0x08) = *(const ma_uint64*)"WAVEfmt ";
	*(ma_uint32*)(pHead + 0x10) = 16; // sub block size
	*(ma_uint16*)(pHead + 0x14) = 1; // PCM
	*(ma_uint16*)(pHead + 0x16) = (ma_uint16)channels;
	*(ma_uint32*)(pHead + 0x18) = sampleRate;
	*(ma_uint32*)(pHead + 0x1c) = sampleRate * channels * sizeof(short); // bytes per second
	*(ma_uint16*)(pHead + 0x20) = (ma_uint16)(channels * sizeof(short)); // bytes per frame
	*(ma_uint16*)(pHead + 0x22) = sizeof(short) * 8; // bits per sample
	*(ma_uint32*)(pHead + 0x24) = *(const ma_uint32*)"data";
	*(ma_uint32*)(pHead + 0x28) = waveSize;
}

// pSrcData: { [1~2] PayLoadSize(<=0x7ff,decodePayloadSize); }* { [PayLoadSize] PayLoadData }*
// EveryPayLoadFrameCount = SampleRate(<=48000)*FrameTime(<=0.12s) <= 5760
//      PreSkipFrameCount = SampleRate(<=48000)*6.5/1000 <= 312 (except restricted-lowdelay application)
extern "C" __declspec(dllexport) int __cdecl maDecodeOpus(const int sampleRate, const int channels, const ma_uint64 frames,
	const ma_uint8* const pSrcData, const ma_uint64 srcDataSize, ma_uint8* const pDstData, const ma_uint64 dstDataSize) {
	if (!pDstData)
		return 1;
	if (dstDataSize < 0x2c)
		return 2;
	if (!pSrcData)
		return 3;
	if (srcDataSize < 1)
		return 4;

	ma_uint64 s = 0;
	int last = 0;
	const ma_uint64 packets = frames > 0 ? (OPUS_PRE_SKIP_FRAMES + frames + OPUS_PACKET_FRAMES - 1) / OPUS_PACKET_FRAMES : 0;
	const ma_uint8* pSrc = pSrcData;
	const ma_uint8* qSrc = pSrc + srcDataSize;
	for (ma_uint64 i = 0; i < packets; i++, s += last) {
		if (pSrc + 2 + s > qSrc || (last = decodePayloadSize(&pSrc, last)) <= 0)
			return 5; // malformed data
	}
	const ma_uint32 payLoadFrameCount = decodeOpusPayloadMs2(pSrc) * sampleRate / 2000; // <=5760
	const ma_uint32 payLoadWaveSize = payLoadFrameCount * channels * sizeof(short); // <=5760*4=23040
	ma_uint32 preSkip = sampleRate * 13 / 2000 * channels * sizeof(short); // <=312*4=1248
	ma_uint8* p = (ma_uint8*)pDstData + 0x2c;
	int err;
	OpusDecoder* const od = opus_decoder_create(sampleRate, channels, &err);
	if (err != OPUS_OK || !od)
		return err & 0xffff;
	opus_decoder_ctl(od, OPUS_SET_COMPLEXITY(10)); // ignore error
	for (qSrc = pSrc, pSrc = pSrcData, s = 0, last = 0; pSrc < qSrc && p + payLoadWaveSize <= (ma_uint8*)pDstData + dstDataSize; s += last) {
		last = decodePayloadSize(&pSrc, last);
		err = opus_decode(od, qSrc + s, last, (opus_int16*)p, payLoadFrameCount, 0);
		if (err != payLoadFrameCount) {
			opus_decoder_destroy(od);
			return (err & 0xffff) + (1 << 16);
		}
		if (preSkip > 0) {
			const ma_uint32 v = preSkip <= payLoadWaveSize ? preSkip : payLoadWaveSize;
			preSkip -= v;
			memmove(p, p + v, payLoadWaveSize - v);
			p += payLoadWaveSize - v;
		} else
			p += payLoadWaveSize;
	}
	opus_decoder_destroy(od);
	writeWavHead(pDstData, sampleRate, channels, (ma_uint32)(frames * channels * sizeof(short)));
	return MA_SUCCESS;
}
